home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / GLX / tablet / tabletglx.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  20.7 KB  |  572 lines

  1. /*
  2.  * Copyright 1991, 1992, 1993, 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. /*
  18.  * tabletglx.c :  a mixed model (GL & X) tablet "line-drawing" demo program.
  19.  *                this program imitates a pen (stylus) drawing on a surface:
  20.  *                while the pen (stylus' button) is pressed down, a line
  21.  *                continues to be drawn.  when the pen is released, the 
  22.  *                current line stops.  
  23.  *
  24.  *                                           ratmandu -- fall 1991
  25.  */
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <X11/Xlib.h>
  29. #include <X11/Xutil.h>
  30. #include <X11/Xos.h>
  31. #include <X11/Xatom.h>
  32. #include <X11/extensions/XI.h>
  33. #include <X11/extensions/XInput.h>
  34. #include <gl/glws.h>
  35. #include <gl/gl.h>
  36.  
  37.  
  38. #define X 0
  39. #define Y 1
  40. #define MAXRESOLUTION 2206
  41.  
  42.    /* line drawing structures to create a new linked-list everytime the
  43.     * stylus (button) is pressed down and grow that list as long as
  44.     * MotionNotify events occur.  when the stylus (button) is released,
  45.     * free up the list and reset the head and tail to point to null
  46.     */
  47. #define newll()  ((struct lineElement *)malloc(sizeof(struct lineElement)))
  48.  
  49. struct lineElement {
  50.     long xy[2];
  51.     struct lineElement *next;
  52. };
  53.  
  54. typedef struct {
  55.     struct lineElement *head, *tail;
  56. } LineList;
  57.  
  58. LineList lineLs;
  59. struct lineElement *lineDummy;
  60.  
  61. Display *dpy;                                    /* The X server connection */
  62. Atom del_atom;                                   /* WM_DELETE_WINDOW atom   */
  63. Window glwin;                                    /* handle to the GL window */
  64.  
  65. static void openwindow(char *);
  66. static void setupdevs();
  67. static Window glx_create_window(Display*, Window, int, int, int, int, char *);
  68. static void makeframe();
  69. static void clean_exit(void);
  70.  
  71.                                        
  72. int tablet_device_id;                            /* device handles for the  */
  73. int tablet_press_type;      /* the following 3 tablet device ID handles are */
  74. int tablet_release_type;    /* defined in setupdevs() and then used in the  */
  75. int tablet_motion_type;     /* infinite (get/process input) loop in main    */
  76.  
  77. float ratio;         /* stores the current ratio to scale the tablet's full */
  78.                      /* range of coordinates into the window's current size */
  79. int xsize, ysize;                    /* stores window's current size values */
  80.  
  81. void main(int argc, char *argv[])
  82. {
  83.     int i;
  84.     long xprev, yprev; /* used if either the x or y stylus val doesn't chng */
  85.     int myExpose, myConfigure, myMotion,
  86.         myButtPress, myButtRelease, myButtDown; /* store which events occur */
  87.  
  88.  
  89.  
  90.     myExpose = myConfigure = myMotion = FALSE;
  91.     myButtPress = myButtRelease = myButtDown = FALSE;
  92.  
  93.     openwindow(argv[0]);          /* open the "window" like winopen wud do */
  94.     setupdevs();           /* make the necessary connections to the tablet */
  95.  
  96.     /*
  97.      * The event loop. 
  98.      */
  99.     while (1) {         /* standard logic:  get event(s), process event(s) */
  100.  
  101.         XEvent event;
  102.         int axis_data[6];
  103.  
  104.         gflush();                        /* For proper DGL performance */
  105.  
  106.     /* this "do while" loop does the `get events' half of the "get events,
  107.      *  process events" action of the infinite while.  this is to ensure
  108.      *  the event queue is always drained before the events that have come
  109.      *  in are processed.
  110.      */
  111.         do {
  112.  
  113.             XNextEvent(dpy, &event);
  114.                 switch (event.type) {
  115.  
  116.             /* "Expose" events are sort of like "REDRAW" in gl-speak in
  117.              *  terms of when a window becomes visible, or a previously
  118.              *  invisible part becomes visible.
  119.              */
  120.                 case Expose:                        /* Exposures */
  121.                     myExpose = TRUE;
  122.                     break;
  123.  
  124.  
  125.             /* "ConfigNotify" events are like "REDRAW" in terms of changes
  126.              *   to a window's size or position.
  127.              */
  128.                 case ConfigureNotify:                /* Resize GL manually */
  129.             /* save the changed width/height of the parent X window */
  130.                     xsize = event.xconfigure.width;
  131.                     ysize = event.xconfigure.height;
  132.                     ratio  = (float) xsize / MAXRESOLUTION;
  133.                     myConfigure = TRUE;
  134.                     break;
  135.  
  136.                 case ButtonRelease:                        /* Back door exit */
  137.                     if (event.xbutton.button == Button1)
  138.                         clean_exit();
  139.                     break;
  140.  
  141.                 case ClientMessage:                        /* WM invoked exit */
  142.                     if (event.xclient.data.l[0] == del_atom)
  143.                         clean_exit();
  144.                     break;
  145.  
  146.                 /* since interest is on the tablet, it becomes the default */
  147.                 default:   
  148.  
  149.                     if ((lineLs.tail != NULL) &&  /* make sure we've already
  150.                                                     processed a button (stylus)
  151.                                                     press event which sets up
  152.                                                     the linked-list for x/y 
  153.                                                     pair storage/line drawing */
  154.  
  155.                         (event.type == tablet_motion_type) &&  /* make sure
  156.                                  this is a mo-
  157.                                  tion event */
  158.  
  159.                          myButtDown) {      /* and make sure the button itself
  160.                           is still down--myButtPress only
  161.                           processes the occurence of the
  162.                           but press (which sets up the new
  163.                           linked list) and then is 
  164.                           immediately reset to FALSE. */
  165.  
  166.                 /* the body of this if statement processes "Motion" events
  167.                  *  from any one of the dials.  the axes_count element of 
  168.                  *  the XDeviceMotionEvent structure (defined in /usr/include
  169.                  *  /X11/extensions/XInput.h) is used to determine if there 
  170.                  *  are 2 or only 1 new coordinate value(s):
  171.                  *  if (axes_count == 2), both X and Y have changed, 
  172.                  *  if (axes_count == 1), only Y has changed--X has not, and
  173.                  *  if (axes_count == 0), only X has changed--Y has not. 
  174.                  */
  175.                         XDeviceMotionEvent *M = (XDeviceMotionEvent *) &event;
  176.  
  177.                         if (M->axes_count != 2) {   /* if x OR y didn't chng */
  178.                             xprev = lineLs.tail->xy[X]; /* axes_count < 2 so */
  179.                             yprev = lineLs.tail->xy[Y]; /* need to save prev */
  180.                         }
  181.                         lineDummy       = newll();    /* alloc a new element */
  182.                         lineDummy->next = NULL;       /* for current line    */
  183.                         lineLs.tail->next = lineDummy;   /* point tail to it */
  184.                         lineLs.tail       = lineDummy;
  185.  
  186.                         if (M->axes_count == 2) {       /* if new x/y pair,  */
  187.  
  188.                             lineLs.tail->xy[X] = M->axis_data[X];
  189.                             lineLs.tail->xy[Y] = M->axis_data[Y];
  190.  
  191.                         } else if (M->first_axis == 0) { /* elseif new X val */
  192.  
  193.                             lineLs.tail->xy[X] = M->axis_data[0];
  194.                             lineLs.tail->xy[Y] = yprev;
  195.  
  196.                         } else if (M->first_axis == 1) { /* elseif new Y val */
  197.  
  198.                             lineLs.tail->xy[X] = xprev;
  199.                             lineLs.tail->xy[Y] = M->axis_data[0];
  200.  
  201.                         }
  202.                         myMotion = TRUE;
  203.                         
  204.                     } else if (event.type == tablet_press_type) {
  205.  
  206.                         XDeviceButtonEvent *P = (XDeviceButtonEvent *) &event;
  207.  
  208.                         xprev = P->axis_data[0]; /* butt's been pressed so  */
  209.                         yprev = P->axis_data[1]; /* now start to make a new */
  210.                         myButtPress = TRUE;      /* line.  this cur pnt'll  */
  211.                         myButtDown = TRUE;       /* be the "move to" coord  */
  212.  
  213.                     } else if (event.type == tablet_release_type) {
  214.  
  215.                         myButtRelease = TRUE;
  216.                         myButtDown = FALSE;
  217.                     }
  218.                     break;
  219.  
  220.             }  /* end switch (event.type) */
  221.  
  222.         } while (XPending(dpy));   /* end "do { } while".
  223.                                     * XPending() is like qtest()--it only
  224.                                     * tells you if there're any events
  225.                                     * presently in the queue.  it does not
  226.                                     * disturb queue's contents in any way.
  227.                                     */
  228.         
  229.     /* On an "Expose" event, redraw the affected window
  230.      */
  231.         if (myExpose) {
  232.             makeframe();                              /* draw the GL stuff */
  233.             myExpose = FALSE;               /* reset flag--queue now empty */
  234.         }
  235.  
  236.     /* On a "ConfigureNotify" event, resize window (XMoveResizeWindow),
  237.      *  and then redraw contents.
  238.      */
  239.         if (myConfigure) {
  240.             XMoveResizeWindow(dpy, glwin, 0, 0, xsize, ysize);
  241.             XSync(dpy, False);                   /* Need before GL reshape */
  242.             viewport(0, (short) (xsize - 1), 0, (short) (ysize - 1));
  243.             makeframe();
  244.             myConfigure = FALSE;            /* reset flag--queue now empty */
  245.         }
  246.  
  247.     /* a motion-type event (the next x/y pair was already saved up above) 
  248.      *  means we're still drawing more along the current line.
  249.      */
  250.         if (myMotion) {
  251.             drawcurrentline();    /* butt still pressed, pen still moving, */
  252.                                   /* keep drawing at end of current line   */
  253.             myMotion = FALSE;               /* reset flag--queue now empty */
  254.         }
  255.  
  256.     /* a "button press"-type event means we're starting a new line so we
  257.      *  need to re-initialize/create our linked-list.
  258.      */
  259.         if (myButtPress) {
  260.             XDeviceButtonEvent *B = (XDeviceButtonEvent *) &event;
  261.  
  262.             lineDummy       = newll();   /* making a new line so start a   */
  263.             lineDummy->next = NULL;      /* new list.  point head and tail */
  264.             lineLs.head = lineDummy;     /* to it, and assign current new  */
  265.             lineLs.tail = lineDummy;     /* point to "tail" of list        */
  266.             lineLs.tail->xy[X] = xprev;  /* make our first point be what   */
  267.             lineLs.tail->xy[Y] = yprev;  /* we saved up above              */
  268.             myButtPress = FALSE;            /* reset flag--queue now empty */
  269.         }
  270.  
  271.     /* a "button release"-type event means the current line is complete
  272.      *  so now we need to free up the current linked-list.
  273.      */
  274.         if (myButtRelease) {
  275.             struct lineElement *ptr;
  276.             for (ptr = lineLs.head; ptr->next != NULL; ptr = ptr->next) {
  277.                 free(ptr);                  /* "empty" our current list    */
  278.             }
  279.             lineLs.head = NULL;
  280.             lineLs.tail = NULL;
  281.             myButtRelease = FALSE;          /* reset flag--queue now empty */
  282.         }
  283.     }
  284. }
  285.  
  286.  
  287.  
  288.  
  289. /*  openwindow -
  290.  *     establish connection to X server, get screen info, specify the
  291.  *     attributes we want the WM to try to provide, and create the GL window
  292.  */
  293. static void openwindow(char *progname) {
  294.  
  295.     int scrnnum;                               /* X screen number            */
  296.     int xorig, yorig;                          /* window (upper-left) origin */
  297.     Window top, windows[2];
  298.     long scrnheight;
  299.     XSizeHints Winhints;                          /* used to fix window size */
  300.  
  301.  
  302.  
  303.    /* Connect to the X server and get screen info */
  304.     if ((dpy = XOpenDisplay(NULL)) == NULL) {
  305.         fprintf(stderr, "%s: cannot connect to X server %s\n",
  306.                                  progname, XDisplayName(NULL));
  307.         exit(1);
  308.     }
  309.     scrnnum = DefaultScreen(dpy);
  310.     scrnheight = DisplayHeight(dpy, scrnnum);
  311.  
  312.    /* define window (upper-left) origin coords */
  313.     xorig = 50;
  314.     yorig = 50;
  315.     xsize = 500;
  316.     ysize = 500;
  317.  
  318.     ratio = (float) xsize / MAXRESOLUTION;   /* calculate ratio to scale 
  319.                                                 full tablet into window */
  320.  
  321.     /* create the top level X window */
  322.     top = XCreateSimpleWindow(dpy, RootWindow(dpy, scrnnum),
  323.                               xorig, yorig, xsize, ysize, 0, 0, 0);
  324.  
  325.    /* specify the values for the Window Size Hints we want to enforce:  this
  326.     *  window's aspect ratio needs to stay at 1:1, constrain min and max
  327.     *  window size, and specify the initial origin and size of the window.
  328.     */
  329.     Winhints.x = xorig;         /* specify desired upper-left corner origin */
  330.     Winhints.y = yorig;         /* of window so prog will place itself      */
  331.     Winhints.width  = xsize;          /* specify desired x/y size of window */
  332.     Winhints.height = ysize;
  333.     Winhints.min_width = xsize/4;                     /* define min and max */
  334.     Winhints.max_width = scrnheight-1;                /* width and height   */
  335.     Winhints.min_height = ysize/4;
  336.     Winhints.max_height = scrnheight-1;
  337.     Winhints.min_aspect.x = 1;                /* keep aspect at a 1:1 ratio */
  338.     Winhints.max_aspect.x = 1;
  339.     Winhints.min_aspect.y = 1;
  340.     Winhints.max_aspect.y = 1;
  341.                                              /* set the corresponding flags */
  342.     Winhints.flags = USPosition|USSize|PMaxSize|PMinSize|PAspect;
  343.     XSetNormalHints(dpy, top, &Winhints);
  344.  
  345.  
  346.    /* create a GL imaging window */
  347.     if ((glwin = glx_create_window(dpy,top,0,0,xsize,ysize,progname)) == NULL) {
  348.         fprintf(stderr, "%s: could not create GL window\n", progname);
  349.         exit(1);
  350.     }
  351.  
  352.    /* define string that will show up in the window title bar (and icon) */
  353.     XStoreName(dpy, top, "tablet mixed model \"line drawing\" program");
  354.  
  355.    /* declare  interest in events we want the window to process */
  356.     XSelectInput(dpy,top,StructureNotifyMask|ButtonPressMask|ButtonReleaseMask);
  357.     XSelectInput(dpy, glwin, StructureNotifyMask|ExposureMask);
  358.  
  359.    /* express interest in WM killing this app */
  360.     if ((del_atom = XInternAtom(dpy, "WM_DELETE_WINDOW", True)) != None)
  361.         XSetWMProtocols(dpy, top, &del_atom, 1);
  362.  
  363.    /* ensure the GL colormap is installed for this app */
  364.     windows[0] = glwin;
  365.     windows[1] = top;
  366.     XSetWMColormapWindows(dpy, top, windows, 2);
  367.  
  368.    /* map the windows */
  369.     XMapWindow(dpy, glwin);
  370.     XMapWindow(dpy, top);
  371. }
  372.  
  373.  
  374.  
  375.  
  376. GLXconfig params[] = {{ 0, 0, 0 }};
  377.  
  378. /* glx_create_window -- Create a singlebuffered, colorindex X window
  379.  *                      suitable for GL imaging.
  380.  *
  381.  * Parameters:   dpy         - X display pointer
  382.  *               parent      - parent window for the GL imaging window
  383.  *               x, y        - window origin relative to parent
  384.  *               w, h        - window width and height
  385.  *               progname    - name of executable
  386.  */
  387. static Window glx_create_window(Display* dpy, Window parent, int x, int y,
  388.                                  int w, int h, char *progname)
  389. {
  390.     GLXconfig *next, *retconfig;
  391.     Colormap cmap = DefaultColormap(dpy, DefaultScreen(dpy));
  392.     XVisualInfo* vis;
  393.     XVisualInfo template;
  394.     XSetWindowAttributes cwa;
  395.     int nret;
  396.     Window win;
  397.  
  398.  
  399.  
  400.    /* Get configuration data for a window based upon all the defaults */
  401.     if ((retconfig = GLXgetconfig(dpy, DefaultScreen(dpy), params)) == NULL) {
  402.         fprintf(stderr,"%s: hdw doesn't support that window type\n",progname);
  403.         exit(1);
  404.     }
  405.  
  406.    /*
  407.     * Scan through config info, pulling info we need to create a window
  408.     * that supports the rendering mode.
  409.     */
  410.     for (next = retconfig; next->buffer; next++) {
  411.         if (next->buffer == GLX_NORMAL) {
  412.             if (next->mode == GLX_COLORMAP) {
  413.                 cmap = next->arg;
  414.             }
  415.             else if (next->mode == GLX_VISUAL) {
  416.                 template.visualid = next->arg;
  417.                 template.screen = DefaultScreen(dpy);
  418.                 vis = XGetVisualInfo(dpy, VisualScreenMask | VisualIDMask,
  419.                                           &template, &nret);
  420.             }
  421.         }
  422.     }
  423.  
  424.    /* Create the window */
  425.     cwa.colormap = cmap;
  426.     cwa.border_pixel = 0;
  427.     win = XCreateWindow(dpy,parent,x,y,w,h,0,vis->depth,
  428.                         InputOutput,vis->visual,CWColormap|CWBorderPixel,&cwa);
  429.  
  430.    /* Rescan config info, find window slot GLXgetconfig provided, fill it
  431.     *  in w/window we just created.
  432.     */
  433.     for (next = retconfig; next->buffer; next++) {
  434.         if ((next->buffer == GLX_NORMAL) && (next->mode == GLX_WINDOW)) {
  435.             next->arg = win;
  436.             break;
  437.         }
  438.     }
  439.  
  440.    /* link to the GL */
  441.     if (GLXlink(dpy, retconfig) < 0) {
  442.         fprintf(stderr, "%s: could not link with the GL\n", progname);
  443.         exit(1);
  444.     }
  445.  
  446.    /* set GL imaging */
  447.     if (GLXwinset(dpy, win) < 0) {
  448.         fprintf(stderr, "%s: could not winset GL window\n", progname);
  449.         exit(1);
  450.     }
  451.  
  452.     return win;
  453. }
  454.  
  455.  
  456.  
  457.  
  458. /*  setupdevs - 
  459.  *
  460.  *   establish a live connection to the tablet device.
  461.  *
  462.  *   leverages off the "X11 Input Extension Library Specification" 
  463.  *   document (you *shud* be able to locate the on-line public access
  464.  *   directory which contains all the files to print hard-copy of this
  465.  *   document under .../mit/doc/extensions/xinput).  refer to
  466.  *   /usr/include/X11/extensions/{XI.h, XInput.h} for structures accessed.
  467.  */
  468. static void setupdevs() {
  469.  
  470.     int i, ndevices;
  471.     XDevice *tablet_device;
  472.     XDeviceInfoPtr lp, list;
  473.     int num_ext_event_classes;
  474.     XEventClass ListOfEventClass[3];
  475.     int tablet_press_class, tablet_release_class, tablet_motion_class;
  476.  
  477.  
  478.  
  479.     /* get a ptr to the list of all currently defined input devices */
  480.     list = (XDeviceInfoPtr) XListInputDevices(dpy, &ndevices);
  481.     if (!list) {
  482.         fprintf(stderr,"XlistInputDevices failed to generate a devices list\n");        exit(1);
  483.     }
  484.  
  485.    /* check out the /usr/people/4Dgifts/examples/devices/input/X/Xlist.c
  486.     *  program (which gets compiled into "xlist").  running it will list
  487.     *  all the currently available input devices on the machine xlist is
  488.     *  run on.  there is a LOT that should be studied in the "input" subtree.
  489.     */
  490.     for (lp=list, i=0; i<ndevices; lp++, i++) {
  491.         if (lp->use == IsXExtensionDevice && strcmp(lp->name,"tablet") == 0) {
  492.                 break;  /* found the right one--now save the ptr (lp) to it */
  493.         }
  494.     }
  495.     if (i == ndevices) {
  496.         fprintf(stderr, "\"tablet\" device not found\n");
  497.         exit(1);
  498.     }
  499.     tablet_device = XOpenDevice(dpy, lp->id);    /* open the Tablet device */
  500.     if (!tablet_device) {
  501.         fprintf(stderr, "XOpenDevice failedfor \"tablet\" device\n");
  502.         exit(1);
  503.     }
  504.     tablet_device_id = tablet_device->device_id;
  505.  
  506.    /* the following 3 macros determine the given event's type and class.
  507.     * each macro is passed the structure that describes the device from
  508.     * which input is desired.
  509.     */
  510.     DeviceButtonPress(tablet_device, tablet_press_type, tablet_press_class);
  511.     DeviceButtonRelease(tablet_device, tablet_release_type, tablet_release_class);
  512.     DeviceMotionNotify(tablet_device, tablet_motion_type, tablet_motion_class);
  513.  
  514.     ListOfEventClass[0]=tablet_press_class;
  515.     ListOfEventClass[1]=tablet_release_class;
  516.     ListOfEventClass[2]=tablet_motion_class;
  517.     num_ext_event_classes = 3;
  518.  
  519.    /* XSelectExtensionEvent requests the server to send events that match
  520.     * the events and devices described by the event list and that come
  521.     * from the requested window.
  522.     */
  523.     XSelectExtensionEvent(dpy, glwin, ListOfEventClass, num_ext_event_classes);
  524.  
  525. }
  526.  
  527.  
  528.  
  529. /*  draw the tablet's current line segment now that event queue is drained.
  530.  */
  531. drawcurrentline()
  532. {
  533.     struct lineElement *ptr;
  534.     long vect[2];
  535.  
  536.     color(CYAN);
  537.  
  538.     bgnline();
  539.         for (ptr = lineLs.head; ptr->next != NULL; ptr = ptr->next) {
  540.             vect[0] = (long) (ptr->xy[0]*ratio);
  541.             vect[1] = (long) (ptr->xy[1]*ratio);
  542.             v2i(vect);
  543.         }
  544.     endline();
  545. }
  546.  
  547.  
  548.  
  549. /*  makeframe -- Draw the tablet "background" in the GL-X window
  550.  */
  551. static void makeframe()
  552. {
  553.     color(BLUE);
  554.     clear();
  555.     ortho2(-0.5, xsize-0.5, -0.5, ysize-0.5);
  556.  
  557.  
  558.     color(YELLOW);
  559.     cmov2i(5, 4);
  560.     charstr("Use left mouse button to quit.");
  561. }
  562.  
  563.  
  564.  
  565. /*  clean_exit  --  Clean up before exiting 
  566.  */
  567. static void clean_exit(void)
  568. {
  569.     XCloseDisplay(dpy);
  570.     exit(0);
  571. }
  572.